<?php

namespace App\Service\Schedule;

use App\Models\Schedule\ScheduleRuleModel;
use App\Models\Schedule\ScheduleUserChangeLogModel;
use App\Models\Schedule\ScheduleUserModel;
use App\Service\BaseService;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

class ScheduleHandleService extends BaseService
{
    private int        $shopId;
    private Collection $ruleModels;

    /**
     * 参数类
     * @var ScheduleParam
     */
    private ScheduleParam $scheduleParam;

    public function __construct(int $shopId)
    {
        $this->shopId = $shopId;

        $this->scheduleParam = new ScheduleParam();
    }

    /**
     * 单次排班
     * @param int $ruleId
     * @param int $date
     * @param array $userIds
     * @return void
     */
    public function single(int $ruleId, int $date, array $userIds)
    {
        $this->scheduleParam
            ->setRuleIds([$ruleId])
            ->setUserIds($userIds)
            ->setStartDate($date)
            ->setEndDate($date)
            ->setType(ScheduleUserModel::TYPE_SINGLE)
            ->setCycle();

        $this->baseFrame();
    }

    /**
     * 组合排班
     * @param array $ruleIds
     * @param int $date
     * @param array $userIds
     * @return void
     */
    public function combine(array $ruleIds, int $date, array $userIds)
    {
        if (count($ruleIds) == 1) {
            $type = ScheduleUserModel::TYPE_SINGLE;
        } elseif (count($ruleIds) == 2) {
            $type = ScheduleUserModel::TYPE_COMBINE;
        } else {
            self::throwErr('组合排班最多支持两个班次');
        }

        $this->scheduleParam
            ->setRuleIds($ruleIds)
            ->setUserIds($userIds)
            ->setStartDate($date)
            ->setEndDate($date)
            ->setType($type)
            ->setCycle();

        $this->baseFrame();
    }

    /**
     * 周期排班
     * @param array $ruleIds
     * @param int $startDate
     * @param int $endDate
     * @param int $userId
     * @return void
     */
    public function cycle(array $ruleIds, int $startDate, int $endDate, int $userId)
    {
        $this->scheduleParam
            ->setRuleIds($ruleIds)
            ->setUserIds([$userId])
            ->setStartDate($startDate)
            ->setEndDate($endDate)
            ->setType(ScheduleUserModel::TYPE_CYCLE)
            ->setCycle();

        $this->baseFrame();
    }

    public function del(int $date, array $userIds)
    {
        $this->scheduleParam
            ->setRuleIds([])
            ->setUserIds($userIds)
            ->setStartDate($date)
            ->setEndDate($date)
            ->setType(ScheduleUserModel::TYPE_DEL)
            ->setCycle();

        $this->baseFrame();
    }

    /**
     * 基础操作框架
     * @return void
     */
    private function baseFrame()
    {
        // 校验ruleId
        $this->getRuleModels($this->scheduleParam->ruleIds);

        // 校验日期
        if (strtotime($this->scheduleParam->startDate) < Carbon::now()->startOfDay()->timestamp) {
            self::throwErr('不允许操作之前的日期');
        }

        DB::beginTransaction();

        foreach ($this->scheduleParam->userIds as $userId) {

            // 清空历史数据
            $beforeDatas = ScheduleUserModel::clear($userId, $this->shopId, $this->scheduleParam->startDate,
                $this->scheduleParam->endDate);

            foreach ($this->scheduleParam->cycles as $cycle) {

                $date   = $cycle['date'];
                $ruleId = $cycle['ruleId'];
                $type   = $cycle['type'];

                $ruleModel = $this->ruleModels[$ruleId] ?? [];
                $beforeData = $beforeDatas->where('date', $date)->first();

//                // 休息
//                if ($ruleId == 0) {
//                    $afterData = [];
//                } else {

                    // 正常排班

                    // 占坑位
                    $afterData = ScheduleUserModel::add($userId, $this->shopId, $date, $ruleId, $ruleModel->name, $ruleModel->start,
                        $ruleModel->end, $type, $ruleModel->color);

                    if ($beforeData) {
                        $ruleId = $beforeData['ruleId'];
                    } else {
                        $ruleId = 0;
                    }
//                }

                // 变更日志
                (new ScheduleUserChangeLogModel())
                    ->setBeforeData($beforeData ? $beforeData->toArray() : [])
                    ->setAfterData($afterData ? $afterData->toArray() : [])
                    ->add($this->shopId, $userId, $date, $ruleId, ScheduleUserChangeLogModel::TYPE_SINGLE);
            }
        }

        DB::commit();
    }

    /**
     * 获取并校验rules
     * @param array $ruleIds
     * @return void
     */
    private function getRuleModels(array $ruleIds)
    {
        $rawRuleIds = [];

        // 兼容组合排班
        foreach ($ruleIds as $ruleId) {
            if (is_int($ruleId)) {
                $rawRuleIds[] = $ruleId;
            } elseif (is_array($ruleId)) {
                $rawRuleIds = array_merge($rawRuleIds, $ruleId);
            } else {
                self::throwErr('错误的rule类型');
            }
        }

        $this->ruleModels = ScheduleRuleModel::query()
                                             ->whereIn('id', $rawRuleIds)
                                             ->whereIn('shopId', [$this->shopId, 0])
                                             ->get()
                                             ->keyBy('id');

        foreach ($rawRuleIds as $ruleId) {
            if (!$this->ruleModels->has($ruleId)) {
                self::throwErr('排班规则不存在');
            }
        }
    }
}

class ScheduleParam
{

    public array $ruleIds;
    public array $userIds;
    public int   $startDate;
    public int   $endDate;
    public array $cycles = [];
    public int   $type;

    /**
     * @param array $ruleIds
     */
    public function setRuleIds(array $ruleIds): self
    {
        $this->ruleIds = $ruleIds;
        return $this;
    }

    /**
     * @param array $userIds
     */
    public function setUserIds(array $userIds): self
    {
        $this->userIds = $userIds;
        return $this;
    }

    /**
     * @param int $startDate
     */
    public function setStartDate(int $startDate): self
    {
        $this->startDate = $startDate;
        return $this;
    }

    /**
     * @param int $endDate
     */
    public function setEndDate(int $endDate): self
    {
        $this->endDate = $endDate;
        return $this;
    }

    public function setType(int $type): self
    {
        $this->type = $type;
        return $this;
    }

    /**
     * @param array $cycles
     */
    public function setCycle(): self
    {
        if (!isset($this->ruleIds) || !isset($this->startDate) || !isset($this->endDate)) {
            BaseService::throwErr('setCycle之前，需设置其他必传参数');
        }

        if (in_array($this->type, [ScheduleUserModel::TYPE_SINGLE, ScheduleUserModel::TYPE_COMBINE])) {
            foreach ($this->ruleIds as $ruleId) {
                $this->cycles[] = [
                    'date'   => $this->startDate,
                    'ruleId' => $ruleId,
                    'type'   => $this->type,
                ];
            }

        } elseif ($this->type == ScheduleUserModel::TYPE_CYCLE) {

            $date = $this->startDate;
            $i    = 0;
            while ($date <= $this->endDate) {

                $ruleId = $this->ruleIds[$i % count($this->ruleIds)];
                if (is_array($ruleId)) {
                    foreach ($ruleId as $id) {
                        $this->cycles[] = [
                            'date'   => $date,
                            'ruleId' => $id,
                            'type'   => ScheduleUserModel::TYPE_COMBINE,
                        ];
                    }
                } else {
                    $this->cycles[] = [
                        'date'   => $date,
                        'ruleId' => $ruleId,
                        'type'   => ScheduleUserModel::TYPE_SINGLE,
                    ];
                }

                $date = (int)date('Ymd', strtotime($date) + 86400);
                $i++;
            }
        } elseif ($this->type == ScheduleUserModel::TYPE_DEL) {
            return $this;
        }

        return $this;
    }


}
